//基于匿名管道通信的进程池
#include<vector>
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string>
#include <sys/wait.h>
#include"Task.h"

#define ERR_CODE -1

  //再组织，对信道的管理变成对数组的增删查改
  class Channel;
    std::vector<Channel> channels;//管理信道

std::vector<task_> Tasks;//管理任务
std::vector<int> ofds;//管理历史打开的fd
void LoadTask()
{
  Tasks.push_back(Task1);
  Tasks.push_back(Task2);
  Tasks.push_back(Task3);
  Tasks.push_back(Task4);
}
//先描述管道
class Channel{//管理信道
    public:
    Channel(int fd,pid_t id,const std::string& name)
    :fd_(fd),id_(id),processname_(name)
    {

    }
    int GetFd()
    {
      return fd_;
    }
    int GetPid()
    {
      return id_;
    }
    const std::string& GetName()
    {
      return processname_;
    }
    ~Channel()
    {}
    private:
    int fd_;//处理任务的文件描述符
    pid_t id_;//子进程pid
    std::string processname_;//当前进程名字

};

int processnum =10;//创建的子进程数量

void HandleTask()
{
  while(true)
  {
    //子进程不停读
    int tasknode=0;//读一个任务码
   ssize_t n= read(0,&tasknode,sizeof(int));//已经把管道读文件描述符重定向到子进程的0号文件描述符（标准输入）
   if(n==sizeof(int))
   {
    //读到一个任务码,执行任务
    if(tasknode>0&&tasknode<=Tasks.size())
    {
      printf("i am a childprocess,get a new task!! task is: %d, my id is:%d\n",tasknode,getpid());
      Tasks[tasknode-1]();
    }
    else{
      printf("tasknode err!!\n");
    }
   }
   else if(n==0)
   {
    //写端关闭
    break;
   }
   else{
    //读出错
    printf("Read error!!!  errprocess is: %d",getpid());
    break;
   }
  }
}
void InitProcessPool()
{
   //批量创建子进程
    for(int i=0;i<processnum;i++)
    {
        //1.为每个子进程创建管道
        int fildes[2]={0};
        int n=pipe(fildes);
        if(n<0){perror("pipe");exit(ERR_CODE);}
      pid_t id= fork();
      if(id==0)
      {
        for(auto e:ofds)
        {
          close(e);//把历史父进程打开的文件描述符关闭掉
        }
        //child 1.父写子读，子关闭写端
        close(fildes[1]);//子进程会继承父进程打开的所有文件
        dup2(fildes[0],0);//往后子进程直接从0号文件读取，往后不需要继续维护filds[0]这个文件描述符
        close(fildes[0]);//重定向后把原文件描述符关闭掉（因为已经不使用，不关闭占据资源）
        HandleTask();//子进程处理任务
        printf("child process exit!!\n");
        exit(0);//如果不退出会怎样？称为孤儿进程？
      }
      else if(id>0)//这里的id返回的是子进程的id
      {
        //farther 父写子读，父关掉读端
        close(fildes[0]);
        //添加字段
        std::string childname="process-"+std::to_string(i+1);
        channels.push_back(Channel(fildes[1],id,childname));
        ofds.push_back(fildes[1]);//记录历史打开的写文件描述符（每次创建子进程都会把它继承下去）
      }
      else{
        //创建子进程失败
        perror("fork");
      }
    }
}
void Menu()
{
  printf("--------------任务菜单------------------\n");
  printf("---1.刷新红BUFF------2.刷新蓝BUFF-------\n");
  printf("---3.刷新CD-----------4.刷新地图--------\n");
  printf("--------------0.退出-------------------\n");
  printf("------贪------玩--------蓝-------月----\n");
}
void CtrlProcess()
{
  int i=0;
  int select=0;
  do{
    printf("请选择：\n");
    Menu();
    scanf("%d",&select);
    printf("select a process:%s, select a task:%d\n",channels[i].GetName().c_str(),select);
   ssize_t n= write(channels[i].GetFd(),&select,sizeof(int));
   if(n<0){perror("write");}
   i++;
   i%=channels.size();
   sleep(1);
  }while(select);
}
void QuitProcess()
{
  for(int i=0;i<channels.size();i++)
  {
    close(channels[i].GetFd());
    int status;
 pid_t n=   waitpid(channels[i].GetPid(),&status,0);
 if(n>0)
 {
  //等待成功
  if(WIFEXITED(status))
  {
   int exitcode=WEXITSTATUS(status);
   printf("Normal exit! exitcode:%d\n",exitcode);
  }
  else{
    printf("Abnormal!!\n");
  }
 }
  }
}
int main()
{
  //1.加载任务
  LoadTask();
  //2.初始化进程池
   InitProcessPool();
  //3.控制进程 -> 选择那个进程？选择那个任务？
  CtrlProcess();
  //4.清理工作
  QuitProcess();
    return 0;
}